﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.Serialization;
using InstantSpine.Core.ParaSpine;

namespace InstantSpine.Core
{
    /// <summary>
    /// FE model specific options like mesh resolution, target
    /// FE solver, selected spine segment
    /// </summary>
    /// <remarks>
    /// Invariants: (int)LastVB > (int)FirstVB
    /// </remarks>
    [DataContract]
    public sealed class FEModelOptions : INotifyPropertyChanged
    {   
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Radial resolution above the nucleus in number of elements
        /// </summary>
        /// <remarks>ParaSpine parameter: radialLineDivsInner</remarks>
        [DataMember]
        public ParaSpineParameter<int> InnerRadialSubdivisions {
            get;
            private set;
        }

        /// <summary>
        /// Radial resolution above the anulus in number of elements (*not including* elements assigned to the cortical shell)
        /// </summary>
        /// <remarks>ParaSpine parameter: radialLineDivs</remarks>
        [DataMember]
        public ParaSpineParameter<int> OuterRadialSubdivisions {
            get;
            private set;
        }

        /// <summary>
        /// Circumferential resolution in number of elements per 90°
        /// </summary>
        /// <remarks>ParaSpine parameter: circumferentialDivs</remarks>        
        [DataMember]
        public ParaSpineParameter<int> CircumferentialSubdivisions {
            get;
            private set;
        }

        /// <summary>
        /// Number of elements allocated for the cortical shell 
        /// </summary>
        /// <remarks>ParaSpine parameter: cortShellDivisions</remarks>
        [DataMember]
        public ParaSpineParameter<int> CorticalShellRadialSubdivisions {
            get;
            private set;
        }

        /// <summary>
        /// Axial resolution of the endplates in number of elements
        /// </summary>
        /// <remarks>ParaSpine parameter: cartLayerDivisions</remarks>
        [DataMember]
        public ParaSpineParameter<int> EndplateAxialSubdivisions {
            get;
            private set;
        }

        /// <summary>
        /// Axial resolution of the endplates in number of elements
        /// </summary>
        /// <remarks>ParaSpine parameter: axialElDivsVert</remarks>
        [DataMember]
        public ParaSpineParameter<int> AxialSubdivisionsVB {
            get;
            private set;
        }

        /// <summary>
        /// Axial resolution of the endplates in number of elements
        /// </summary>
        /// <remarks>ParaSpine parameter: axialElDivsDisc</remarks>
        [DataMember]
        public ParaSpineParameter<int> AxialSubdivisionsDisc {
            get;
            private set;
        }

        /// <summary>
        /// ID of first meshed VB (1-based index, i.e. T1 = 1 and S1 = 18)
        /// </summary>
        /// <remarks>ParaSpine paramter: meshVertFrom (1-based index)</remarks>
        [DataMember]
        public ParaSpineParameter<int> FirstVB {
            get;
            private set;
        }

        /// <summary>
        /// ID of last meshed VB (1-based index, i.e. T1 = 1 and S1 = 18)
        /// </summary>
        /// <remarks>ParaSpine paramter: meshVertTo</remarks>
        [DataMember]
        public ParaSpineParameter<int> LastVB {
            get;
            private set;
        }

        [DataMember]
        TargetFESolver target;
        public TargetFESolver Target {
            get { return target; }
            set {
                if (target != value) {
                    target = value;
                    PropertyChanged.Raise(this);
                }
            }
        }                    

        public FEModelOptions() {
            InnerRadialSubdivisions = new ParaSpineParameter<int>("radialLineDivsInner", 4);
            OuterRadialSubdivisions = new ParaSpineParameter<int>("radialLineDivs", 6);
            CircumferentialSubdivisions = new ParaSpineParameter<int>("circumferentialDivs", 8);
            CorticalShellRadialSubdivisions = new ParaSpineParameter<int>("cortShellDivisions", 2);
            EndplateAxialSubdivisions = new ParaSpineParameter<int>("cartLayerDivisions", 2);
            AxialSubdivisionsVB = new ParaSpineParameter<int>("axialElDivsVert", 6);
            AxialSubdivisionsDisc = new ParaSpineParameter<int>("axialElDivsDisc", 3);
            Target = TargetFESolver.Abaqus;
            FirstVB = new ParaSpineParameter<int>("meshVertFrom", 15, 
                // ensure that FirstVB < LastVB
                newVal => newVal < LastVB.ParameterValue);
            LastVB = new ParaSpineParameter<int>("meshVertTo", 16, 
                // ensure that LastVB < FirstVB
                newVal => newVal > FirstVB.ParameterValue);
        }

        /// <summary>
        /// Initializes parameter constraint predicates which
        /// could not be serialized
        /// </summary>
        [OnDeserialized]
        void InitConstraints(StreamingContext _) {            
            FirstVB.CanChangeValue = newVal => newVal < LastVB.ParameterValue;
            LastVB.CanChangeValue = newVal => newVal > FirstVB.ParameterValue;
        }    
    }
}
